package com.crm.aspect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import cn.hutool.core.util.ObjectUtil;
import com.crm.annotation.Audit;
import com.crm.model.AuditMessage;
import com.crm.model.User;
import com.crm.service.IAuditMessageService;
import com.crm.service.IUserService;
import com.crm.uilt.Moudle;
import com.crm.uilt.Operate;
import com.crm.uilt.ResponseContent;
import com.crm.uilt.RestResult;
import com.crm.uilt.reflect.BaseValueReturn;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * @author zjd
 * @Title:
 * @Description:
 * @date 2021/4/718:00
 */
@Slf4j
@Aspect
@Component
public class AuditAop {

    @Autowired
    private IAuditMessageService auditService;
    @Autowired
    private IUserService userService;

    // 操作发起者
    ThreadLocal<User> user = new ThreadLocal<>();
    // 操作应用
    ThreadLocal<Integer> appId = new ThreadLocal<>();
    // 功能模块
    ThreadLocal<Integer> moudleCode = new ThreadLocal<>();
    // 操作类型
    ThreadLocal<Integer> operateType = new ThreadLocal<>();
    // IP地址
    ThreadLocal<String> ip = new ThreadLocal<>();
    // 操作时间点
   // ThreadLocal<String> operateTime = new ThreadLocal<>();
    // 操作信息实体
    ThreadLocal<AuditMessage> msg = new ThreadLocal<>();
    // 对CIS，额外的菜单id信息extension
    ThreadLocal<String> extension = new ThreadLocal<>();

    // 声明AOP切入点
    @Pointcut("@annotation(com.crm.annotation.Audit)")
    public void audit() {
    }

    @Before("audit()")
    public void beforeExec(JoinPoint joinPoint) {
    }

    @After("audit()")
    public void afterExec(JoinPoint joinPoint) {
    }

    @Around("audit()")
    public Object aroundExec(ProceedingJoinPoint pjp) throws Throwable {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            extension.set(request.getParameter("extension"));
           // operateTime.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
            ip.set(getIpAddr(request));
            if (ObjectUtil.isNotEmpty(request.getParameter("appId"))){
                appId.set(Integer.valueOf(request.getParameter("appId")));
            }
            MethodSignature ms = (MethodSignature) pjp.getSignature();
            Method method = ms.getMethod();
            // 获取注解的参数信息
            Audit auditAnno = method.getAnnotation(Audit.class);
            moudleCode.set(auditAnno.moudleCode());
            Class<?> external = auditAnno.extension();
            Constructor<?> cons = external.getConstructor(Integer.class, HttpServletRequest.class);
            BaseValueReturn bvr = (BaseValueReturn) cons.newInstance(moudleCode.get(), request);
            Map<String, Object> reqInfo = bvr.getRequestInfo();
            Integer operate_type = Integer.valueOf(reqInfo.get(BaseValueReturn.OPT) + "");
            operateType.set(operate_type);
            Object target = reqInfo.get(BaseValueReturn.TARGET);
            // 获取当前登录的用户，需注意：登录时没有msgKey
            String msgKey = request.getParameter("msgKey");
            User loginUser = null;
            if (operate_type.equals(Operate.LOGIN)) {
               /* List<User> users = userService.selectByEmail(String.valueOf(target));
                if (users != null && !users.isEmpty()) {
                    loginUser = users.get(0);
                }*/
            } else if (msgKey != null) {
               // loginUser = userService.selectMsgFromRedisPurely(msgKey);
            }
            user.set(loginUser);

            AuditMessage am = new AuditMessage();
            // am.setUserId需判空，代表过期
            if (loginUser != null) {
                am.setUserId(loginUser.getId());
            } else {
                am.setUserId(-1L);
            }
            am.setAppId(appId.get());
            am.setMoudleCode(moudleCode.get());
            am.setOperateType(operateType.get());
            am.setIp(ip.get());
            // TODO details=target
            String details = "";
            if (moudleCode.get() == Moudle.DATA && loginUser != null) {
                details = (String) target;
            } else {
                // TODO 其他模块
            }
            //am.setTarget(details);
            msg.set(am);
        } catch (Exception e) {
            log.error("Error occured while auditing, cause by: ", e);
        }
        Object rtn = pjp.proceed();
        return rtn;
    }


    /**
     * 带参返回
     */
    @AfterReturning(pointcut = "audit()", returning = "response")
    public void doAfterReturning(RestResult response) {
        try {
            // 返回码SUCCESS才去审计记录
            if (HttpStatus.OK.value()==response.getCode()) {
                if (null == msg.get() && extension.get() != null) {
                    log.warn("Warning occured, because msg is null!");
                } else if (!(operateType.get() == Operate.FETCH_DATA
                        && (extension.get() == null || extension.get().isEmpty()))) {
                    auditService.save(msg.get());

                }
            }
        } catch (Exception e) {
            log.warn("Warning occured while afterReturning, cause by: ", e);
        }
    }



    /**
     * 不带参返回
     */
    @AfterReturning(pointcut = "audit()")
    public void doAfterReturning(JoinPoint joinPoint) {
        log.debug("afterReturning without returnType..");
    }

    @AfterThrowing(pointcut = "audit()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        log.error("Error occured, cause by {}", e.getMessage());
    }

    private String getRemoteHost(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;
    }



    /**
     * 获取用户真实IP地址，不使用request.getRemoteAddr()的原因是有可能用户使用了代理软件方式避免真实IP地址,
     * 可是，如果通过了多级反向代理的话，X-Forwarded-For的值并不止一个，而是一串IP值
     *
     * @return ip
     */
    private String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值，第一个ip才是真实ip
            if( ip.indexOf(",")!=-1 ){
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }


}
